<?php
namespace soa\http;

class agent {

	/**
	 * @var string
	 */
	public $agent = NULL;

	/**
	 * @var bool
	 */
	public $is_browser = FALSE;

	/**
	 * @var bool
	 */
	public $is_robot = FALSE;

	/**
	 * @var bool
	 */
	public $is_mobile = FALSE;

	/**
	 * @var array
	 */
	public $languages = [];

	/**
	 * @var array
	 */
	public $charsets = [];

	/**
	 * @var array
	 */
	public $platforms = [];

	/**
	 * @var array
	 */
	public $browsers = [];

	/**
	 * @var array
	 */
	public $mobiles = [];

	/**
	 * @var array
	 */
	public $robots = [];

	/**
	 * @var string
	 */
	public $platform = '';

	/**
	 * @var string
	 */
	public $browser = '';

	/**
	 * @var string
	 */
	public $version = '';

	/**
	 * @var string
	 */
	public $mobile = '';

	/**
	 * @var string
	 */
	public $robot = '';

	/**
	 * @var	mixed
	 */
	public $referer;

	/**
	 * @return	void
	 */
	public function __construct()
	{
		if (isset($_SERVER['HTTP_USER_AGENT']))
		{
			$this->agent = trim($_SERVER['HTTP_USER_AGENT']);
		}
		else
		{
			$this->is_robot = true;
			$this->is_browser = false;
			$this->is_mobile = false;
		}

		if ($this->agent !== NULL && $this->_load_agent_file())
		{
			$this->_compile_data();
		}
	}

	// --------------------------------------------------------------------

	/**
	 * @return	bool
	 */
	protected function _load_agent_file()
	{
		$this->platforms = [
			'windows nt 10.0'	=> 'Windows 10',
			'windows nt 6.3'	=> 'Windows 8.1',
			'windows nt 6.2'	=> 'Windows 8',
			'windows nt 6.1'	=> 'Windows 7',
			'windows nt 6.0'	=> 'Windows Vista',
			'windows nt 5.2'	=> 'Windows 2003',
			'windows nt 5.1'	=> 'Windows XP',
			'windows nt 5.0'	=> 'Windows 2000',
			'windows nt 4.0'	=> 'Windows NT 4.0',
			'winnt4.0'			=> 'Windows NT 4.0',
			'winnt 4.0'			=> 'Windows NT',
			'winnt'				=> 'Windows NT',
			'windows 98'		=> 'Windows 98',
			'win98'				=> 'Windows 98',
			'windows 95'		=> 'Windows 95',
			'win95'				=> 'Windows 95',
			'windows phone'			=> 'Windows Phone',
			'windows'			=> 'Unknown Windows OS',
			'android'			=> 'Android',
			'blackberry'		=> 'BlackBerry',
			'iphone'			=> 'iOS',
			'ipad'				=> 'iOS',
			'ipod'				=> 'iOS',
			'os x'				=> 'Mac OS X',
			'ppc mac'			=> 'Power PC Mac',
			'freebsd'			=> 'FreeBSD',
			'ppc'				=> 'Macintosh',
			'linux'				=> 'Linux',
			'debian'			=> 'Debian',
			'sunos'				=> 'Sun Solaris',
			'beos'				=> 'BeOS',
			'apachebench'		=> 'ApacheBench',
			'aix'				=> 'AIX',
			'irix'				=> 'Irix',
			'osf'				=> 'DEC OSF',
			'hp-ux'				=> 'HP-UX',
			'netbsd'			=> 'NetBSD',
			'bsdi'				=> 'BSDi',
			'openbsd'			=> 'OpenBSD',
			'gnu'				=> 'GNU/Linux',
			'unix'				=> 'Unknown Unix OS',
			'symbian' 			=> 'Symbian OS'
		];

		$this->browsers = [
			'OPR'			=> 'Opera',
			'Flock'			=> 'Flock',
			'Edge'			=> 'Spartan',
			'Chrome'		=> 'Chrome',
			// Opera 10+ always reports Opera/9.80 and appends Version/<real version> to the user agent string
			'Opera.*?Version'	=> 'Opera',
			'Opera'			=> 'Opera',
			'MSIE'			=> 'Internet Explorer',
			'Internet Explorer'	=> 'Internet Explorer',
			'Trident.* rv'	=> 'Internet Explorer',
			'Shiira'		=> 'Shiira',
			'Firefox'		=> 'Firefox',
			'Chimera'		=> 'Chimera',
			'Phoenix'		=> 'Phoenix',
			'Firebird'		=> 'Firebird',
			'Camino'		=> 'Camino',
			'Netscape'		=> 'Netscape',
			'OmniWeb'		=> 'OmniWeb',
			'Safari'		=> 'Safari',
			'Mozilla'		=> 'Mozilla',
			'Konqueror'		=> 'Konqueror',
			'icab'			=> 'iCab',
			'Lynx'			=> 'Lynx',
			'Links'			=> 'Links',
			'hotjava'		=> 'HotJava',
			'amaya'			=> 'Amaya',
			'IBrowse'		=> 'IBrowse',
			'Maxthon'		=> 'Maxthon',
			'Ubuntu'		=> 'Ubuntu Web Browser'
		];


		$this->mobiles = [
			// legacy array, old values commented out
			'mobileexplorer'	=> 'Mobile Explorer',
//  'openwave'			=> 'Open Wave',
//	'opera mini'		=> 'Opera Mini',
//	'operamini'			=> 'Opera Mini',
//	'elaine'			=> 'Palm',
			'palmsource'		=> 'Palm',
//	'digital paths'		=> 'Palm',
//	'avantgo'			=> 'Avantgo',
//	'xiino'				=> 'Xiino',
			'palmscape'			=> 'Palmscape',
//	'nokia'				=> 'Nokia',
//	'ericsson'			=> 'Ericsson',
//	'blackberry'		=> 'BlackBerry',
//	'motorola'			=> 'Motorola'

			// Phones and Manufacturers
			'motorola'		=> 'Motorola',
			'nokia'			=> 'Nokia',
			'palm'			=> 'Palm',
			'iphone'		=> 'Apple iPhone',
			'ipad'			=> 'iPad',
			'ipod'			=> 'Apple iPod Touch',
			'sony'			=> 'Sony Ericsson',
			'ericsson'		=> 'Sony Ericsson',
			'blackberry'	=> 'BlackBerry',
			'cocoon'		=> 'O2 Cocoon',
			'blazer'		=> 'Treo',
			'lg'			=> 'LG',
			'amoi'			=> 'Amoi',
			'xda'			=> 'XDA',
			'mda'			=> 'MDA',
			'vario'			=> 'Vario',
			'htc'			=> 'HTC',
			'samsung'		=> 'Samsung',
			'sharp'			=> 'Sharp',
			'sie-'			=> 'Siemens',
			'alcatel'		=> 'Alcatel',
			'benq'			=> 'BenQ',
			'ipaq'			=> 'HP iPaq',
			'mot-'			=> 'Motorola',
			'playstation portable'	=> 'PlayStation Portable',
			'playstation 3'		=> 'PlayStation 3',
			'playstation vita'  	=> 'PlayStation Vita',
			'hiptop'		=> 'Danger Hiptop',
			'nec-'			=> 'NEC',
			'panasonic'		=> 'Panasonic',
			'philips'		=> 'Philips',
			'sagem'			=> 'Sagem',
			'sanyo'			=> 'Sanyo',
			'spv'			=> 'SPV',
			'zte'			=> 'ZTE',
			'sendo'			=> 'Sendo',
			'nintendo dsi'	=> 'Nintendo DSi',
			'nintendo ds'	=> 'Nintendo DS',
			'nintendo 3ds'	=> 'Nintendo 3DS',
			'wii'			=> 'Nintendo Wii',
			'open web'		=> 'Open Web',
			'openweb'		=> 'OpenWeb',

			// Operating Systems
			'android'		=> 'Android',
			'symbian'		=> 'Symbian',
			'SymbianOS'		=> 'SymbianOS',
			'elaine'		=> 'Palm',
			'series60'		=> 'Symbian S60',
			'windows ce'	=> 'Windows CE',

			// Browsers
			'obigo'			=> 'Obigo',
			'netfront'		=> 'Netfront Browser',
			'openwave'		=> 'Openwave Browser',
			'mobilexplorer'	=> 'Mobile Explorer',
			'operamini'		=> 'Opera Mini',
			'opera mini'	=> 'Opera Mini',
			'opera mobi'	=> 'Opera Mobile',
			'fennec'		=> 'Firefox Mobile',

			// Other
			'digital paths'	=> 'Digital Paths',
			'avantgo'		=> 'AvantGo',
			'xiino'			=> 'Xiino',
			'novarra'		=> 'Novarra Transcoder',
			'vodafone'		=> 'Vodafone',
			'docomo'		=> 'NTT DoCoMo',
			'o2'			=> 'O2',

			// Fallback
			'mobile'		=> 'Generic Mobile',
			'wireless'		=> 'Generic Mobile',
			'j2me'			=> 'Generic Mobile',
			'midp'			=> 'Generic Mobile',
			'cldc'			=> 'Generic Mobile',
			'up.link'		=> 'Generic Mobile',
			'up.browser'	=> 'Generic Mobile',
			'smartphone'	=> 'Generic Mobile',
			'cellphone'		=> 'Generic Mobile'
		];

		$this->robots = [
			'googlebot'		=> 'Googlebot',
			'msnbot'		=> 'MSNBot',
			'baiduspider'		=> 'Baiduspider',
			'bingbot'		=> 'Bing',
			'slurp'			=> 'Inktomi Slurp',
			'yahoo'			=> 'Yahoo',
			'ask jeeves'		=> 'Ask Jeeves',
			'fastcrawler'		=> 'FastCrawler',
			'infoseek'		=> 'InfoSeek Robot 1.0',
			'lycos'			=> 'Lycos',
			'yandex'		=> 'YandexBot',
			'mediapartners-google'	=> 'MediaPartners Google',
			'CRAZYWEBCRAWLER'	=> 'Crazy Webcrawler',
			'adsbot-google'		=> 'AdsBot Google',
			'feedfetcher-google'	=> 'Feedfetcher Google',
			'curious george'	=> 'Curious George'
		];

		return true;
	}

	// --------------------------------------------------------------------

	/**
	 * @return	bool
	 */
	protected function _compile_data()
	{
		$this->_set_platform();

		foreach (array('_set_robot', '_set_browser', '_set_mobile') as $function)
		{
			if ($this->$function() === TRUE)
			{
				break;
			}
		}
	}

	// --------------------------------------------------------------------

	/**
	 * @return	bool
	 */
	protected function _set_platform()
	{
		if (is_array($this->platforms) && count($this->platforms) > 0)
		{
			foreach ($this->platforms as $key => $val)
			{
				if (preg_match('|'.preg_quote($key).'|i', $this->agent))
				{
					$this->platform = $val;
					return TRUE;
				}
			}
		}

		$this->platform = 'Unknown Platform';
		return FALSE;
	}

	// --------------------------------------------------------------------

	/**
	 * @return	bool
	 */
	protected function _set_browser()
	{
		if (is_array($this->browsers) && count($this->browsers) > 0)
		{
			foreach ($this->browsers as $key => $val)
			{
				if (preg_match('|'.$key.'.*?([0-9\.]+)|i', $this->agent, $match))
				{
					$this->is_browser = TRUE;
					$this->version = $match[1];
					$this->browser = $val;
					$this->_set_mobile();
					return TRUE;
				}
			}
		}

		return FALSE;
	}

	// --------------------------------------------------------------------

	/**
	 * @return	bool
	 */
	protected function _set_robot()
	{
		if (is_array($this->robots) && count($this->robots) > 0)
		{
			foreach ($this->robots as $key => $val)
			{
				if (preg_match('|'.preg_quote($key).'|i', $this->agent))
				{
					$this->is_robot = TRUE;
					$this->robot = $val;
					$this->_set_mobile();
					return TRUE;
				}
			}
		}

		return FALSE;
	}

	// --------------------------------------------------------------------

	/**
	 * @return	bool
	 */
	protected function _set_mobile()
	{
		if (is_array($this->mobiles) && count($this->mobiles) > 0)
		{
			foreach ($this->mobiles as $key => $val)
			{
				if (FALSE !== (stripos($this->agent, $key)))
				{
					$this->is_mobile = TRUE;
					$this->mobile = $val;
					return TRUE;
				}
			}
		}

		return FALSE;
	}

	// --------------------------------------------------------------------

	/**
	 * Set the accepted languages
	 *
	 * @return	void
	 */
	protected function _set_languages()
	{
		if ((count($this->languages) === 0) && ! empty($_SERVER['HTTP_ACCEPT_LANGUAGE']))
		{
			$this->languages = explode(',', preg_replace('/(;\s?q=[0-9\.]+)|\s/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE']))));
		}

		if (count($this->languages) === 0)
		{
			$this->languages = array('Undefined');
		}
	}

	// --------------------------------------------------------------------

	/**
	 * @return	void
	 */
	protected function _set_charsets()
	{
		if ((count($this->charsets) === 0) && ! empty($_SERVER['HTTP_ACCEPT_CHARSET']))
		{
			$this->charsets = explode(',', preg_replace('/(;\s?q=.+)|\s/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_CHARSET']))));
		}

		if (count($this->charsets) === 0)
		{
			$this->charsets = array('Undefined');
		}
	}

	// --------------------------------------------------------------------

	/**
	 * @param	string	$key
	 * @return	bool
	 */
	public function is_browser($key = NULL)
	{
		if ( ! $this->is_browser)
		{
			return FALSE;
		}

		// No need to be specific, it's a browser
		if ($key === NULL)
		{
			return TRUE;
		}

		// Check for a specific browser
		return (isset($this->browsers[$key]) && $this->browser === $this->browsers[$key]);
	}

	// --------------------------------------------------------------------

	/**
	 * @param	string	$key
	 * @return	bool
	 */
	public function is_robot($key = NULL)
	{
		if ( ! $this->is_robot)
		{
			return FALSE;
		}


		// No need to be specific, it's a robot
		if ($key === NULL)
		{
			return TRUE;
		}

		// Check for a specific robot
		return (isset($this->robots[$key]) && $this->robot === $this->robots[$key]);
	}

	// --------------------------------------------------------------------

	/**
	 * @param	string	$key
	 * @return	bool
	 */
	public function is_mobile($key = NULL)
	{
		if ( ! $this->is_mobile)
		{
			return FALSE;
		}

		// No need to be specific, it's a mobile
		if ($key === NULL)
		{
			return TRUE;
		}

		// Check for a specific robot
		return (isset($this->mobiles[$key]) && $this->mobile === $this->mobiles[$key]);
	}

	// --------------------------------------------------------------------

	/**
	 * @return	bool
	 */
	public function is_referral()
	{
		if ( ! isset($this->referer))
		{
			if (empty($_SERVER['HTTP_REFERER']))
			{
				$this->referer = FALSE;
			}
			else
			{
				$referer_host = @parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST);
				$own_host = parse_url(config_item('base_url'), PHP_URL_HOST);

				$this->referer = ($referer_host && $referer_host !== $own_host);
			}
		}

		return $this->referer;
	}

	// --------------------------------------------------------------------

	/**
	 * @return	string
	 */
	public function agent_string()
	{
		return $this->agent;
	}

	// --------------------------------------------------------------------

	/**
	 * @return	string
	 */
	public function platform()
	{
		return $this->platform;
	}

	// --------------------------------------------------------------------

	/**
	 * @return	string
	 */
	public function browser()
	{
		return $this->browser;
	}

	// --------------------------------------------------------------------

	/**
	 * @return	string
	 */
	public function version()
	{
		return $this->version;
	}

	// --------------------------------------------------------------------

	/**
	 * @return	string
	 */
	public function robot()
	{
		return $this->robot;
	}
	// --------------------------------------------------------------------

	/**
	 * @return	string
	 */
	public function mobile()
	{
		return $this->mobile;
	}

	// --------------------------------------------------------------------

	/**
	 * @return	bool
	 */
	public function referrer()
	{
		return empty($_SERVER['HTTP_REFERER']) ? '' : trim($_SERVER['HTTP_REFERER']);
	}

	// --------------------------------------------------------------------

	/**
	 * @return	array
	 */
	public function languages()
	{
		if (count($this->languages) === 0)
		{
			$this->_set_languages();
		}

		return $this->languages;
	}

	// --------------------------------------------------------------------

	/**
	 * @return	array
	 */
	public function charsets()
	{
		if (count($this->charsets) === 0)
		{
			$this->_set_charsets();
		}

		return $this->charsets;
	}

	// --------------------------------------------------------------------

	/**
	 * @param	string	$lang
	 * @return	bool
	 */
	public function accept_lang($lang = 'en')
	{
		return in_array(strtolower($lang), $this->languages(), TRUE);
	}

	// --------------------------------------------------------------------

	/**
	 * @param	string	$charset
	 * @return	bool
	 */
	public function accept_charset($charset = 'utf-8')
	{
		return in_array(strtolower($charset), $this->charsets(), TRUE);
	}

	// --------------------------------------------------------------------

	/**
	 * @param	string	$string
	 * @return	void
	 */
	public function parse($string)
	{
		// Reset values
		$this->is_browser = FALSE;
		$this->is_robot = FALSE;
		$this->is_mobile = FALSE;
		$this->browser = '';
		$this->version = '';
		$this->mobile = '';
		$this->robot = '';

		// Set the new user-agent string and parse it, unless empty
		$this->agent = $string;

		if ( ! empty($string))
		{
			$this->_compile_data();
		}
	}

}
